/**
 * This class defines an Xml document.
 * It contaisn the inner xml structure.
 */
function XmlDocument() {

  this.rootElement = null;

}

/**
 * This class defines a single Xml element.
 */
function XmlElement(name) {
  
  this.name = name;

}

/**
 * This class lets you parse an XML document.
 */
function XmlParser() {

  var matchAttribute = function (str) {
    return str.match("^([_\\-a-zA-Z0-9]+)=\"([:%/,\\._+\\-a-zA-Z0-9 ]*)\"");
  }

  var setAttribute = function (obj, match) {
    var attName = match[1];
    var attValue = match[2];
    obj[attName] = attValue;
  }

  var innerParseElement = function (xmlstring) {
    var matches = xmlstring.match("^<([_\\-a-zA-Z0-9]+)");
    if (matches == null) {
      throw new Error ("Bad XML format: don't know how to parse: " + xmlstring);
    }

    var element = new XmlElement(matches[1]);
    xmlstring = xmlstring.substring(matches[1].length + 1);

    while (xmlstring.search("/>") != 0) {
      if (xmlstring[0] == " " || xmlstring[0] == "\t") {
        xmlstring = xmlstring.substring(1);
      } if (xmlstring[0] == ">") {
        xmlstring = xmlstring.substring(1);
        
        while (xmlstring.search("</" + element.name + ">") > 0) {
          var child = innerParseElement(xmlstring);
          element[child.result.name] = child.result;
          xmlstring = child.remaining;
        }
        return {result: element, remaining: xmlstring.substring(3 + element.name.length)};

      } else if ((match = matchAttribute(xmlstring)) != null) {
        setAttribute(element, match);
        xmlstring = xmlstring.substring(match[0].length);
      } else {
        throw new Error("Unknown format exception, don't know how to parse: " + xmlstring);
      }
    }
    return {result: element, remaining: xmlstring.substring(2)};
  }

  this.parse = function(xmlstring) {
    var doc = new XmlDocument();

    while (xmlstring.search("^<\\?xml") == 0) {
      xmlstring = xmlstring.substring(5);
      while (xmlstring.search("^\\?>") != 0) {
        if (xmlstring[0] == " " || xmlstring[0] == "\t") {
          xmlstring = xmlstring.substring(1);
        } else if ((match = matchAttribute(xmlstring)) != null) {
          setAttribute(doc, match);
          xmlstring = xmlstring.substring(match[0].length);
        } else {
          throw new Error("Unknown format exception, don't know how to parse: " + xmlstring);
        }
      }
      xmlstring = xmlstring.substring(2);
    }

    var result = innerParseElement(xmlstring);
    if (result.remaining != "" ) {
      throw new Error("Bad XML format: leftovers after root element");
    }
    doc.rootElement = result.result;

    return doc;
  }

}
